import EventEmitter from 'events'
import {IDRSampleList} from './media-segment-info'

class MSEController {
    constructor(config) {
        this._config = config
        this._emitter = new EventEmitter()

        if (
            this._config.isLive &&
            this._config.autoCleanupSourceBuffer == undefined
        ) {
            // For live stream, do auto cleanup by default
            this._config.autoCleanupSourceBuffer = true
        }

        this.e = {
            onSourceOpen: this._onSourceOpen.bind(this),
            onSourceEnded: this._onSourceEnded.bind(this),
            onSourceClose: this._onSourceClose.bind(this),
            onSourceBufferError: this._onSourceBufferError.bind(this),
            onSourceBufferUpdateEnd: this._onSourceBufferUpdateEnd.bind(this)
        }

        this._mediaSource = null
        this._mediaSourceObjectURL = null
        this._mediaElement = null

        this._isBufferFull = false
        this._hasPendingEos = false

        this._requireSetMediaDuration = false
        this._pendingMediaDuration = 0

        this._pendingSourceBufferInit = []
        this._mimeTypes = {
            video: null,
            audio: null
        }
        this._sourceBuffers = {
            video: null,
            audio: null
        }
        this._lastInitSegments = {
            video: null,
            audio: null
        }
        this._pendingSegments = {
            video: [],
            audio: []
        }
        this._pendingRemoveRanges = {
            video: [],
            audio: []
        }
        this._idrList = new IDRSampleList()
    }

    destroy() {
        if (this._mediaElement || this._mediaSource) {
            this.detachMediaElement()
        }
        this.e = null
        this._emitter.removeAllListeners()
        this._emitter = null
    }

    on(event, listener) {
        this._emitter.addListener(event, listener);
    }

    off(event, listener) {
        this._emitter.removeListener(event, listener);
    }

    attachMediaElement(mediaElement) {
        if (this._mediaSource) {
            throw new Error('MediaSource has been attached to an HTMLMediaElement')
        }
        let ms = this._mediaSource = new window.MediaSource()
        ms.addEventListener('sourceopen', this.e.onSourceOpen)
        ms.addEventListener('sourceended', this.e.onSourceEnded)
        ms.addEventListener('sourceclose', this.e.onSourceClose)

        this._mediaElement = mediaElement
        this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource)
        mediaElement.src = this._mediaSourceObjectURL
    }

    detachMediaElement() {
        if (this._mediaSource) {
            let ms = this._mediaSource
            for (let type in this._sourceBuffers) {
                // pending segments should be discard
                let ps = this._pendingSegments[type]
                ps.slice(0, ps.length)
                this._pendingSegments[type] = null
                this._pendingRemoveRanges[type] = null
                this._lastInitSegments[type] = null

                // remove all sourcebuffers
                let sb = this._sourceBuffers[type]
                if (sb) {
                    if (ms.readyState !== 'closed') {   // media source还没挂载到media element
                        // ms edge can throw an error: Unexpected call to method or property access
                        try {
                            ms.removeSourceBuffer(sb)
                        } catch (error) {
                            console.error('MSEController', error.message)
                        }
                        sb.removeEventListener('error', this.e.onSourceBufferError)
                        sb.removeEventListener('updateend', this.e.onSourceBufferUpdateEnd)
                    }
                    this._mimeTypes[type] = null
                    this._sourceBuffers[type] = null
                }
            }
            if (ms.readyState === 'open') {
                // media source已经挂载到media element
                try {
                    ms.endOfStream()
                } catch (error) {
                    console.error('MSEController', error.message)
                }
                ms.removeEventListener('sourceopen', this.e.onSourceOpen)
                ms.removeEventListener('sourceended', this.e.onSourceEnded)
                ms.removeEventListener('sourceclose', this.e.onSourceClose)
                this._pendingSourceBufferInit = []
                this._isBufferFull = false
                this._idrList.clear()
                this._mediaSource = null
            }
            if (this._mediaElement) {
                this.mediaElement.src = ''
                this.mediaElement.removeAttribute('src')
                this.mediaElement = null
            }
            if (this._mediaSourceObjectURL) {
                // 释放内存
                window.URL.revokeObjectURL(this._mediaSourceObjectURL)
                this._mediaSourceObjectURL = null
            }
        }
    }
}

export default MSEController